#include "Texture.hpp"
#include <iostream>

void Texture::Bind()
{
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex);
}

void Texture::Unbind()
{
    //glActiveTexture(0);
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

Texture::Texture(unsigned int width, unsigned int height)
{
	glEnable(GL_TEXTURE_2D);
	glActiveTexture(GL_TEXTURE0);
	glGenTextures(1, &tex);
	glBindTexture(GL_TEXTURE_2D, tex);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	glBindTexture(GL_TEXTURE_2D, 0);
	
	
	glActiveTexture(GL_TEXTURE0 + 1);
	glGenTextures(1, &tex_depth);
	glBindTexture(GL_TEXTURE_2D, tex_depth);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
	glBindTexture(GL_TEXTURE_2D, 0);
	
	glGenFramebuffers(1, &fbo);
	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
	
	glGenRenderbuffers(1, &rbo);
	glBindRenderbuffer(GL_RENDERBUFFER, rbo);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
	glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0);
	
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);

	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex_depth, 0);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
	Unbind();
}


void Texture::RenderTo()
{
	glBindFramebuffer(GL_FRAMEBUFFER, fbo);

}

void Texture::RenderToScreen(const Shader& sh)
{

    sh.Use();
    Unbind();
    //glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
	glDisable(GL_DEPTH_TEST);
	
	GLuint t1Location = glGetUniformLocation(sh.GetProgram(), "fbo_texture");
    GLuint t2Location = glGetUniformLocation(sh.GetProgram(), "depth_texture");

    glUniform1i(t1Location, 0);
    glUniform1i(t2Location, 1);
	
	glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, tex_depth);
	
	glBindTexture(GL_TEXTURE_2D, 0);
	glBegin(GL_QUADS);
		glVertex3f(-1,-1, 1.0);
		glVertex3f(-1,1, 1.0);
		glVertex3f(1,1, 1.0);
		glVertex3f(1,-1, 1.0);
	glEnd();

    Unbind();
    Shader::UseNoShader();
}

void Texture::RenderToTexture(const Shader& sh, Texture& textureIn)
{

    sh.Use();
    textureIn.RenderTo();
    glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
	glDisable(GL_DEPTH_TEST);
	
	GLuint t1Location = glGetUniformLocation(sh.GetProgram(), "fbo_texture");
    GLuint t2Location = glGetUniformLocation(sh.GetProgram(), "depth_texture");

    glUniform1i(t1Location, 0);
    glUniform1i(t2Location, 1);
	
	glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, tex_depth);
	
	glBindTexture(GL_TEXTURE_2D, 0);
	//if(loc == -1)
		//std::cout << tex << std::endl;
	glBegin(GL_QUADS);
		glVertex3f(-1,-1, 1.0);
		glVertex3f(-1,1, 1.0);
		glVertex3f(1,1, 1.0);
		glVertex3f(1,-1, 1.0);
	glEnd();

    Unbind();
    Shader::UseNoShader();
}

void Texture::RenderToScreen()
{
	RenderToScreen(*Shader::PassthroughShader);
}

void Texture::CopyToTexture(Texture& texture)
{
	RenderToTexture(*Shader::PassthroughShaderPost, texture);
}


void Texture::Blend(Texture& Sharp, Texture& Blurred, Texture& Target, Shader& BlendShader)
{
	
	GLuint t1Location = glGetUniformLocation(BlendShader.GetProgram(), "sharp_texture");
    GLuint t2Location = glGetUniformLocation(BlendShader.GetProgram(), "blurred_texture");
	
	BlendShader.Use();
	glUniform1i(t1Location, 0);
    glUniform1i(t2Location, 1);
	
	glActiveTexture(GL_TEXTURE0);
    //glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, Sharp.GetTexture());

    glActiveTexture(GL_TEXTURE0 + 1);
    glBindTexture(GL_TEXTURE_2D, Blurred.GetTexture());
	
	//glActiveTexture(GL_TEXTURE0);
	//glBindTexture(GL_TEXTURE_2D, 0);
	
	
	Target.RenderTo();
	glBegin(GL_QUADS);
		glVertex3f(-1,-1, 1.0);
		glVertex3f(-1,1, 1.0);
		glVertex3f(1,1, 1.0);
		glVertex3f(1,-1, 1.0);
	glEnd();

    Unbind();
    Shader::UseNoShader();
}







